﻿using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using Flux.Data.Tilesets;
using Flux.Editor.Extensions;
using Flux.Editor.Messaging;

namespace Flux.Editor.Controls
{
	public partial class TilesetPanel
	{
		#region Fields

		private readonly List<Tileset> _tilesets = new List<Tileset>();
		private int _mouseX;
		private int _mouseY;

		#endregion

		#region Properties

		public Tileset SelectedTileset { get; private set; }

		#endregion

		#region Initialization

		public TilesetPanel()
		{
			InitializeComponent();
			if (this.IsInDesignMode()) return;
			App.Messenger.Register(Tokens.RefreshTilesetPanel, Refresh);
		}

		#endregion

		#region Refresh / Draw

		public void Refresh(object message = null)
		{
			if (message != null)
			{
				cboTilesets.Items.Clear();

				if (message is Tileset)
				{
					AddTileset((Tileset) message);
				}

				foreach (var tileset in _tilesets)
				{
					cboTilesets.Items.Add(tileset.Name);
				}
			}

			Clear();
			DrawTileset();
			DrawGrid();
			DrawCursor();
		}

		public void Clear()
		{
			canvas.Children.Clear();
		}

		private void DrawGrid()
		{
			if (SelectedTileset == null) return;

			var pw = canvas.ActualWidth; 
			var ph = canvas.ActualHeight;

			for (var y = 0; y < ph; y += SelectedTileset.TileHeight)
			{
				var line = CreateGridLine(0, y, pw, y);
				line.IsHitTestVisible = false;
				canvas.Children.Add(line);
			}

			for (var x = 0; x < pw; x += SelectedTileset.TileWidth)
			{
				var line = CreateGridLine(x, 0, x, ph);
				line.IsHitTestVisible = false;
				canvas.Children.Add(line);
			}
		}

		private void DrawTileset()
		{
			if (SelectedTileset == null) return;

			var uri = new Uri(SelectedTileset.ImagePath, UriKind.RelativeOrAbsolute);
			var bmp = new BitmapImage(uri);
			var img = new Image
			{
				Source = bmp,
				Stretch = Stretch.None,
				IsHitTestVisible = false
			};

			SelectedTileset.ImageWidth = (int)bmp.Width;
			SelectedTileset.ImageHeight = (int)bmp.Height;

			canvas.Children.Add(img);
		}

		private void DrawCursor()
		{
			if (SelectedTileset == null) return;

			var rect = new Rectangle
			{
				StrokeThickness = 1,
				Stroke = Brushes.Red,
				StrokeDashArray = new DoubleCollection { 2, 1 },
				Width = SelectedTileset.TileWidth,
				Height = SelectedTileset.TileHeight,
				IsHitTestVisible = false
			};

			var x = (_mouseX / SelectedTileset.TileWidth);
			var y = (_mouseY / SelectedTileset.TileHeight);
			var index = y * (SelectedTileset.ImageWidth / SelectedTileset.TileWidth) + x;

			txtTileInfo.Text = "Index: " + index;
			
			canvas.Children.Add(rect);

			var mx = (_mouseX / SelectedTileset.TileWidth) * SelectedTileset.TileWidth;
			var my = (_mouseY / SelectedTileset.TileHeight) * SelectedTileset.TileHeight;
			Canvas.SetLeft(rect, mx);
			Canvas.SetTop(rect, my);
		}

		#endregion

		#region Methods

		public void AddTileset(Tileset tileset)
		{
			_tilesets.Add(tileset);
		}

		private Line CreateGridLine(double x, double y, double ex, double ey)
		{
			var line = new Line
			{
				X1 = x,
				Y1 = y,
				X2 = ex,
				Y2 = ey,
				StrokeThickness = 1,
				Stroke = Brushes.White,
				StrokeDashArray = new DoubleCollection {2, 1},
				IsHitTestVisible = false
			};
			return line;
		}

		#endregion

		#region Events

		private void TilesetsSelectionChanged(object sender, SelectionChangedEventArgs e)
		{
			var index = cboTilesets.SelectedIndex;
			if (index == -1) return;
			SelectedTileset = _tilesets[index];
			App.Messenger.Send(Tokens.UpdateSelectedTileset, SelectedTileset);

			Refresh();
		}

		private void TilesetPanel_OnSizeChanged(object sender, SizeChangedEventArgs e)
		{
			Refresh();
		}

		private void Canvas_OnMouseMove(object sender, MouseEventArgs e)
		{
			var pos = e.GetPosition((IInputElement)sender);
			_mouseX = (int)pos.X;
			_mouseY = (int)pos.Y;

			Refresh();
		}

		private void Canvas_OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
		{
			var x = (_mouseX / SelectedTileset.TileWidth);
			var y = (_mouseY / SelectedTileset.TileHeight);
			var index = y * (SelectedTileset.ImageWidth / SelectedTileset.TileWidth) + x;
			dynamic tileInfo = new ExpandoObject();
			tileInfo.X = x;
			tileInfo.Y = y;
			tileInfo.Index = index;
			App.Messenger.Send(Tokens.UpdateLeftSelectedTile, tileInfo);
		}

		private void Canvas_OnMouseRightButtonDown(object sender, MouseButtonEventArgs e)
		{
			var x = (_mouseX / SelectedTileset.TileWidth);
			var y = (_mouseY / SelectedTileset.TileHeight);
			var index = y * (SelectedTileset.ImageWidth / SelectedTileset.TileWidth) + x;
			dynamic tileInfo = new ExpandoObject();
			tileInfo.X = x;
			tileInfo.Y = y;
			tileInfo.Index = index;
			App.Messenger.Send(Tokens.UpdateRightSelectedTile, tileInfo);
		}
		
		#endregion
	}
}